package com.gt.util.db;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidPooledConnection;
import com.alibaba.fastjson.JSONObject;
import com.gt.util.prop.PropUtil;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;

import java.sql.Connection;

/**
 * @author WUREN
 * @desc 数据库连接生成类，返回数据库连接对象
 * 所有读取 gtxy.jdbc.properties 文件
 * @create 2019-01-30 14:36
 **/
public class DBConnection {
    private Logger logger = Logger.getLogger(DBConnection.class);

    private static DruidDataSource dataSource=null;
    // 数据库配置文件 classpath:gtxy.jdbc.properties
    private PropUtil propKit = new PropUtil(DBConstant.CONFIG_FILE_NAME);

    /**
     * 数据库配置名称，根据 gtxy.jdbc.properties 文件中的属性前缀区分
     */
    private String dbName="";
    private String url;
    private String driverClassName;
    private String userName;
    private String password;

    /**
     * 设置自动提交
     */
    private boolean defaultAutoCommit = true;

    /**
     * 初始化连接数
     */
    private int initialSize;
    /**
     * 最小连接数
     */
    private int minIdle;
    // 最大连接数
    private int maxActive;
    /**
     * 获取连接超时时间，单位毫秒
     */
    private int maxWait;
    /**
     * 启用非公平锁，当设置maxWait属性后，使用非公平锁可提高并发效率
     */
    private boolean useUnfairLock;
    /**
     * 连接有效性检测时间间隔，检测需要关闭的空闲连接，单位毫秒
     */
    private int timeBetweenEvictionRunsMillis;
    /**
     * 连接在池中最小空闲的时间，单位毫秒
     */
    private int minEvictableIdleTimeMillis;
    /**
     * 获取连接检测
     */
    private boolean testOnBorrow;
    /**
     * 归还连接检测
     */
    private boolean testOnReturn;
    /**
     * 在获取连接后，确定是否要进行连接空闲时间的检查。如果配置为true，则如果连接的空闲时间大于timeBetweenEvictionRunsMillis，会执行validationQuery检测连接是否有效
     */
    private boolean testWhileIdle;
    /**
     * 是否缓存preparedStatement，对支持游标的数据库性能提升巨大
     */
    private boolean poolPreparedStatements;
    /**
     * 指定每个连接上PSCache数量
     */
    private int maxPoolPreparedStatementPerConnectionSize;
    /**
     * 对于长时间不使用的连接进行强制关闭
     */
    private boolean removeAbandoned;
    /**
     * 连接空闲时间超过30分钟则执行强制关闭；单位为秒
     */
    private int removeAbandonedTimeout;
    /**
     * 关闭空闲连接时输出错误日志 ，方便定位程序中哪个连接池没有关闭 （会影响性能，生产环境建议关闭）
     */
    private boolean logAbandoned;

    /**
     * 慢SQL记录
     */
    private boolean mergeSql;
    private boolean logSlowSql;
    private int slowSqlMillis;



    /**
     * 构造函数完成数据库的连接和连接对象的生成
     */
    public DBConnection() {
        if(null == propKit){
            logger.error(DBConstant.CONFIG_FILE_NAME+"文件不存在");
            return;
        }

        this.dbName = "default";
        url = propKit.get("jdbc.url");
        driverClassName = propKit.get("jdbc.driverClassName");
        userName = propKit.get("jdbc.userName");
        password = propKit.get("jdbc.password");

        initialSize = Integer.valueOf(propKit.get("jdbc.initialSize","10"));
        minIdle = Integer.valueOf(propKit.get("jdbc.minIdle","10"));
        maxActive = Integer.valueOf(propKit.get("jdbc.maxActive","300"));
        maxWait = Integer.valueOf(propKit.get("jdbc.maxWait","10000"));
        useUnfairLock = BooleanUtils.toBoolean(propKit.get("jdbc.useUnfairLock","true"));
        timeBetweenEvictionRunsMillis = Integer.valueOf(propKit.get("jdbc.timeBetweenEvictionRunsMillis","60000"));
        minEvictableIdleTimeMillis = Integer.valueOf(propKit.get("jdbc.minEvictableIdleTimeMillis","60000"));
        testOnBorrow = BooleanUtils.toBoolean(propKit.get("jdbc.testOnBorrow","false"));
        testOnReturn = BooleanUtils.toBoolean(propKit.get("jdbc.testOnReturn","false"));
        testWhileIdle = BooleanUtils.toBoolean(propKit.get("jdbc.testWhileIdle","true"));
        poolPreparedStatements = BooleanUtils.toBoolean(propKit.get("jdbc.poolPreparedStatements","true"));
        maxPoolPreparedStatementPerConnectionSize = Integer.valueOf(propKit.get("jdbc.maxPoolPreparedStatementPerConnectionSize","200"));
        removeAbandoned = BooleanUtils.toBoolean(propKit.get("jdbc.removeAbandoned","true"));
        removeAbandonedTimeout = Integer.valueOf(propKit.get("jdbc.removeAbandonedTimeout","1800"));
        logAbandoned = BooleanUtils.toBoolean(propKit.get("jdbc.logAbandoned","false"));

        mergeSql = BooleanUtils.toBoolean(propKit.get("jdbc.mergeSql","true"));
        logSlowSql = BooleanUtils.toBoolean(propKit.get("jdbc.logSlowSql","true"));
        slowSqlMillis = Integer.valueOf(propKit.get("jdbc.slowSqlMillis","2000"));
    }

    /**
     *  格式如下：
     *  db1.jdbc.url=xxxx
     *  db1.jdbc.userName=xxxx
     */
    public DBConnection(String dbName) {
        // 数据库配置文件 classpath:gtxy.jdbc.properties
        if(null == propKit){
            logger.error(DBConstant.CONFIG_FILE_NAME+"文件不存在");
            return;
        }

        this.dbName = dbName;
        String prefix = "";
        if(!StringUtils.isEmpty(dbName)){
            prefix = dbName+".";
        }

        url = propKit.get(prefix+"jdbc.url");
        driverClassName = propKit.get(prefix+"jdbc.driverClassName");
        userName = propKit.get(prefix+"jdbc.userName");
        password = propKit.get(prefix+"jdbc.password");
        initialSize = Integer.valueOf(propKit.get(prefix+"jdbc.initialSize","10"));
        minIdle = Integer.valueOf(propKit.get(prefix+"jdbc.minIdle","10"));
        maxActive = Integer.valueOf(propKit.get(prefix+"jdbc.maxActive","300"));
        maxWait = Integer.valueOf(propKit.get(prefix+"jdbc.maxWait","10000"));
        useUnfairLock = BooleanUtils.toBoolean(propKit.get(prefix+"jdbc.useUnfairLock","true"));
        timeBetweenEvictionRunsMillis = Integer.valueOf(propKit.get(prefix+"jdbc.timeBetweenEvictionRunsMillis","60000"));
        minEvictableIdleTimeMillis = Integer.valueOf(propKit.get(prefix+"jdbc.minEvictableIdleTimeMillis","60000"));
        testOnBorrow = BooleanUtils.toBoolean(propKit.get(prefix+"jdbc.testOnBorrow","false"));
        testOnReturn = BooleanUtils.toBoolean(propKit.get(prefix+"jdbc.testOnReturn","false"));
        testWhileIdle = BooleanUtils.toBoolean(propKit.get(prefix+"jdbc.testWhileIdle","true"));
        poolPreparedStatements = BooleanUtils.toBoolean(propKit.get(prefix+"jdbc.poolPreparedStatements","true"));
        maxPoolPreparedStatementPerConnectionSize = Integer.valueOf(propKit.get(prefix+"jdbc.maxPoolPreparedStatementPerConnectionSize","200"));
        removeAbandoned = BooleanUtils.toBoolean(propKit.get(prefix+"jdbc.removeAbandoned","true"));
        removeAbandonedTimeout = Integer.valueOf(propKit.get(prefix+"jdbc.removeAbandonedTimeout","1800"));
        logAbandoned = BooleanUtils.toBoolean(propKit.get(prefix+"jdbc.logAbandoned","false"));

        mergeSql = BooleanUtils.toBoolean(propKit.get(prefix+"jdbc.mergeSql","true"));
        logSlowSql = BooleanUtils.toBoolean(propKit.get(prefix+"jdbc.logSlowSql","true"));
        slowSqlMillis = Integer.valueOf(propKit.get(prefix+"jdbc.slowSqlMillis","2000"));
    }

    /**
     * 初始化dataSource
     * @throws Exception
     */
    public void init() throws Exception {
        if(dataSource==null){
            if(StringUtils.isEmpty(url) || StringUtils.isEmpty(driverClassName) ||
                    StringUtils.isEmpty(userName) || StringUtils.isEmpty(password)) {
                logger.error("数据库参数不完整！");
            }

            dataSource=new DruidDataSource();
            dataSource.setUrl(url);
            dataSource.setDriverClassName(driverClassName);
            dataSource.setUsername(userName);
            dataSource.setPassword(password);
            dataSource.setInitialSize(initialSize);
            dataSource.setMinIdle(minIdle);
            dataSource.setMaxActive(maxActive);
            dataSource.setMaxWait(maxWait);
            dataSource.setUseUnfairLock(useUnfairLock);
            dataSource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
            dataSource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
            dataSource.setTestOnBorrow(testOnBorrow);
            dataSource.setTestOnReturn(testOnReturn);
            dataSource.setTestWhileIdle(testWhileIdle);
            dataSource.setPoolPreparedStatements(poolPreparedStatements);
            dataSource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);
            dataSource.setRemoveAbandoned(removeAbandoned);
            dataSource.setRemoveAbandonedTimeout(removeAbandonedTimeout);
            dataSource.setLogAbandoned(logAbandoned);
            dataSource.setValidationQuery("SELECT 1 FROM DUAL");
            dataSource.setDefaultAutoCommit(defaultAutoCommit);
        }
    }

    /**
     * 获取dataSource
     * @return
     * @throws Exception
     */
    public DruidDataSource getDBSource() throws Exception {
        synchronized(this) {
            if(null == dataSource){
                logger.info(">>>>>>>>>>>>>>>>> 【"+dbName+"】初始化DBSource");
                init();
            }
        }
        return dataSource;
    }

    /**
     * 获取数据库连接
     * @return
     * @throws Exception
     */
    public Connection getConnection() throws Exception {
        Connection con = getDBSource().getConnection();
//        System.out.println("["+Thread.currentThread().getId()+"]:createCount："+getDBSource().getCreateCount());
//        System.out.println("["+Thread.currentThread().getId()+"]:connectCount："+getDBSource().getConnectCount());
//        System.out.println("["+Thread.currentThread().getId()+"]:closeCount："+getDBSource().getCloseCount());

        return con;

//        return getDBSource().getConnection();
    }
}
